package com.qrlib.utils;

import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.view.Surface;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Created by Xiz on May 26, 2017.
 */

public class CameraUtils {

  public static Camera open() {
    return open(-1);
  }

  public static Camera open(int id) {
    try {
      if (id < 0) {
        return Camera.open();
      } else {
        return Camera.open(id);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  public static int getRotation(int rotation, int cameraId) {
    Camera.CameraInfo info = new Camera.CameraInfo();
    Camera.getCameraInfo(cameraId, info);
    int degrees = 0;
    switch (rotation) {
      case Surface.ROTATION_0:
        degrees = 0;
        break;
      case Surface.ROTATION_90:
        degrees = 90;
        break;
      case Surface.ROTATION_180:
        degrees = 180;
        break;
      case Surface.ROTATION_270:
        degrees = 270;
        break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
      result = (info.orientation + degrees) % 360;
      result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
      result = (info.orientation - degrees + 360) % 360;
    }
    return result;
  }

  /**
   * 优先选择 1080x1920 1280x720
   */
  public static Camera.Size getOptimalPreviewSize(@NonNull Camera camera, int targetW,
      int targetH) {
    List<Size> sizes;
    try {
      sizes = camera.getParameters().getSupportedPreviewSizes();
      StringBuilder b = new StringBuilder();
      for (Size size: sizes) {
        if (b.length() != 0) {
          b.append(", ");
        }
        b.append(toString(size));
      }
      LogUtils.i("camera supported preview sizes: " + b.toString());
    } catch (Exception e) {
      return camera.new Size(targetW, targetH);
    }

    // find 1080 1920
    for (Size size: sizes) {
      if ((size.width == 1920 && size.height == 1080)
          || (size.width == 1280 && size.height == 720)) {
        LogUtils.i("uses preview size: " + toString(size));
        return size;
      }
    }

    final double ASPECT_TOLERANCE = 0.1f;
    double targetRatio = (double) targetW / targetH;
    Camera.Size optimalSize = null;

    // Try to find an size match aspect ratio and size
    double minDiff = Double.MAX_VALUE;
    int minH = targetH;
    for (Camera.Size size: sizes) {
      double ratio = (double) size.width / size.height;
      if (Math.abs(ratio - targetRatio) < ASPECT_TOLERANCE
          && Math.abs(size.height - minH) < minDiff) {
        optimalSize = size;
        minDiff = Math.abs(size.height - minH);
      }
    }

    // Cannot find the one match the aspect ratio, ignore the requirement
    minDiff = Double.MAX_VALUE;
    minH = targetH;
    if (optimalSize == null) {
      for (Camera.Size size: sizes) {
        if (Math.abs(size.height - minH) < minDiff) {
          optimalSize = size;
          minDiff = Math.abs(size.height - minH);
        }
      }
    }
    return optimalSize;
  }

  private static String toString(Size size) {
    return String.valueOf(size.width) + "x" + size.height;
  }

  public static boolean isFlashLightSupported(Camera camera) {
    if (camera != null) {
      try {
        Camera.Parameters params = camera.getParameters();
        List<String> modes = params.getSupportedFlashModes();
        return params.getFlashMode() != null && modes != null && modes
            .contains(Parameters.FLASH_MODE_TORCH);
      } catch (Exception e) {
        return false;
      }
    } else {
      return false;
    }
  }

  public interface Callback {

    void onCameraOpened(Camera camera);
  }

  private static Executor exec = Executors.newSingleThreadExecutor(new ThreadFactory() {
    @Override
    public Thread newThread(@NonNull Runnable r) {
      return new Thread(r, "camera");
    }
  });

  /**
   * NOTE 系统返回了非空的 camera 时，有可能拿到的是没有链接的实例。所以添加了显示的 reconnect。并且在5秒内重试10次。否则fail
   */
  public static void openAsync(final int facing, final @NonNull Callback callback) {
    exec.execute(new Runnable() {
      @Override
      public void run() {
        Camera camera = null;
        int retryCount = 10;
        while (retryCount-- > 0) {
          try {
            camera = open(facing);
            if (camera != null) {
              camera.lock();
              break;
            }
          } catch (Throwable e) {
            e.printStackTrace();
          }
          SystemClock.sleep(500L);
        }
        callback.onCameraOpened(camera);
      }
    });
  }
}
